/*
  Copyright 2002-2003 The University of Texas at Austin
  
    Authors: Anthony Thane <thanea@ices.utexas.edu>
             Vinay Siddavanahalli <skvinay@cs.utexas.edu>
    Advisor: Chandrajit Bajaj <bajaj@cs.utexas.edu>

  This file is part of Volume Rover.

  Volume Rover is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  Volume Rover is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with iotree; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include <QFile>
#include <QtAlgorithms>
#include "colormap.h"

using namespace PBVP;

const int ColorMap::MAP_SIZE = 256;

ColorMap::ColorMap()
{
	m_ArrayRGBA = new unsigned char[4*MAP_SIZE];
	// add two default ColorMapNode: blue at 0 and red at 1
	m_colorNodeList.append(ColorMapNode(0.0, Color(0.0f, 0.0f, 1.0f, 0.0f)));
	m_colorNodeList.append(ColorMapNode(1.0, Color(1.0f, 0.0f, 0.0f, 0.5f)));
	AddNode(0.5, 0.0, 1.0, 0.0);
	//AddNode(0.75, 1.0, 1.0, 0.0);

	calcRGBA();
}

ColorMap::ColorMap(const ColorMap& copy)
{
	m_ArrayRGBA = new unsigned char[4*MAP_SIZE];
	m_colorNodeList = copy.m_colorNodeList;
	memcpy(m_ArrayRGBA, copy.m_ArrayRGBA, 4*MAP_SIZE);
}

ColorMap ColorMap::operator=(const ColorMap& copy)
{
	if (this == &copy)
	{
		return *this;
	}
	m_colorNodeList = copy.m_colorNodeList;
	memcpy(m_ArrayRGBA, copy.m_ArrayRGBA, 4*MAP_SIZE);

	return *this;
}

ColorMap::~ColorMap()
{
	delete[] m_ArrayRGBA;
}


void ColorMap::AddNode(double pos, float fRed, float fGreen, float fBlue)
{
	assert(pos >= 0.0 && pos <= 1.0);
	ColorMapNode node(pos, Color(fRed, fGreen, fBlue));
	m_colorNodeList.append(node);
	qSort(m_colorNodeList);
}

void ColorMap::AddNode(double pos)
{
	Color color = getColor(pos);
	ColorMapNode node(pos, color);
	m_colorNodeList.append(node);
	qSort(m_colorNodeList);
}

void ColorMap::DeleteNode(int index)
{
	if (index < size()-1 && index > 0) {
		m_colorNodeList.removeAt(index);
	}
}

void ColorMap::MoveNode(int index, double fPosition)
{
	if(index > 0 && index < size()-1) {
		m_colorNodeList[index].position = fPosition;
		qSort(m_colorNodeList);
	}
}

int ColorMap::size() const
{
	return m_colorNodeList.size();
}

double ColorMap::GetPosition(int index) const
{
	assert(index >= 0 && index < size());
	return m_colorNodeList[index].position;
}

Color ColorMap::getColor(double pos)
{
	Color color;
	QList<ColorMapNode>::Iterator i = m_colorNodeList.begin();
	while ((*i).position < pos)
	{
		++i;
	}
	if(i == m_colorNodeList.begin()) {
		// The first node in the list
		color = (*i).color;
	} else {
		Color c2 = (*i).color;
		double p2 = (*i).position;
		--i;
		Color c1 = (*i).color;
		double p1 = (*i).position;
		if(p1 == p2) {
			color = c1;
		} else {
			double alpha = (p2-pos) / (p2-p1);
			color = Color::linterp(c1, c2, alpha);
		}
	}
	return color;
}

float ColorMap::GetRed(int index) const
{
	assert(index >= 0 && index < size());
	return m_colorNodeList[index].color.r;
}

float ColorMap::GetGreen(int index) const
{
	assert(index >= 0 && index < size());
	return m_colorNodeList[index].color.g;
}

float ColorMap::GetBlue(int index) const
{
	assert(index >= 0 && index < size());
	return m_colorNodeList[index].color.b;
}

void ColorMap::ChangeColor(int index, float fRed,  float fGreen,  float fBlue)
{
	m_colorNodeList[index].color = Color(fRed, fGreen, fBlue);
}


/********************

Alphamap
No of nodes
< no of nodes >
Position and opacity
< Position A1 >
< Position AN >
< Position A2 >
< Position A3 >
...
< Position AN-1 >

*********************/
void ColorMap::saveMap( QTextStream& stream )
{
	stream << "ColorMap\n";
	stream << "Number of nodes\n";
	stream << size() << "\n";
	
	stream << "Position and RGB\n";

	for(int i = 0; i < size(); i++) {
		ColorMapNode node = m_colorNodeList[i];
		stream << node.position << " "  << node.red() << " "  << node.green() << " "  << node.blue()<< "\n";
	}
	
}

void ColorMap::loadMap( QTextStream& stream )
{
	double position;
	float red, green, blue;
	int size;
	QString junk;

	m_colorNodeList.clear();

	stream.skipWhiteSpace();
	junk = stream.readLine(); // ColorMap

	stream.skipWhiteSpace();
	junk = stream.readLine(); // Number of nodes
	stream >> size;

	stream.skipWhiteSpace();
	junk = stream.readLine(); // Position and R G B
	for(int i = 0; i < size; i++) {
		stream >> position >> red >> green >> blue;
		ColorMapNode node(position, Color(red, green, blue));
		m_colorNodeList.append(node);
	}
	qSort(m_colorNodeList);
}

void ColorMap::removeSandwichedNodes()
{
	// when there are 3 or more nodes with the same position
	// remove the ones in the middle

	if (size() < 3) { // with size < 3, cant have sandwiched nodes
		return;
	}

	int i = 1;
	while(i < size()-1)
	{
		ColorMapNode prev = m_colorNodeList[i-1];
		ColorMapNode next = m_colorNodeList[i+1];
		ColorMapNode curr = m_colorNodeList[i];
		if(curr.position == prev.position && curr.position == next.position)
		{
			m_colorNodeList.removeAt(i);
		} else {
			i++;
		}
	}
}

void ColorMap::resetNodes()
{
	if (size() < 3) { // with size < 3, cant have nodes in between to delete
		return;
	}

	QList<ColorMapNode>::Iterator start = m_colorNodeList.begin(); ++start;
	QList<ColorMapNode>::Iterator end = m_colorNodeList.end(); --end;
	m_colorNodeList.erase(start, end);

	// make the whole thing white!
	ChangeColor(0, 1.0f, 1.0f, 1.0f);
	ChangeColor(1, 1.0f, 1.0f, 1.0f);
}

void ColorMap::calcRGBA()
{
	int nCNode = m_colorNodeList.size();
	int *nodePos = new int[nCNode];
	nodePos[0] = 0; nodePos[nCNode-1] = 255;
	int i, next = 0;
	for(i = 1; i < nCNode-1; i++) {
		int n = m_colorNodeList[i].position * 255;
		nodePos[i] = n;
	}
	for(i = 0; i <= 255; i++) {
		if (i >= nodePos[next])
		{
			m_colorNodeList[next].color.toUChar4(&(m_ArrayRGBA[4*i]));
			next++;
		} else {
			// linear interpolation between next and (next-1) node
			Color c = Color::linterp(m_colorNodeList[next-1].color, m_colorNodeList[next].color, 
				float(nodePos[next]-i)/(nodePos[next]-nodePos[next-1]));
			c.toUChar4(&(m_ArrayRGBA[4*i]));
		}
	}
	delete[] nodePos;
}